package checker;

import java.util.ArrayList;
import java.util.HashMap;

import jsc.base.JSCerror;
import jsc.base.WordEnumerator;

/**
 * @author user
 *
 */
public class Checker {

	private DictionarySet dictionaries;
	private WordEnumerator parser;
	private HashMap<String, ArrayList<String>> errors;

	/**
	 * @param d The DictionarySet used to correct words
	 * @param s The text to correct
	 */
	public Checker(DictionarySet d, String s) {
		this.dictionaries = d;
		this.parser = new WordEnumerator(s);
		this.errors = new HashMap<String, ArrayList<String>>();
	}

	/**
	 * @param d The DictionarySet used to correct words
	 */
	public Checker(DictionarySet d) {
		this.dictionaries = d;
	}

	/**
	 * @return An HashMap containing errors associated to their corrections
	 */
	public HashMap<String, ArrayList<String>> getErrors() {
		return this.errors;
	}

	/**
	 * @param error The word to search
	 * @return true if the word is an error
	 */
	public boolean isAnError(String error) {
		return this.errors.containsKey(error);
	}

	/**
	 * @throws JSCerror
	 * checks all the text, and builds the errors HashMap 
	 */
	public void check() throws JSCerror {
		ArrayList<String> suggests = new ArrayList<String>();
		while (!this.parser.noMoreWord()) {
			String word = this.parser.getWord();
			suggests = new ArrayList<String>();
			if (!this.dictionaries.contains(word) && !this.dictionaries.contains(word.toLowerCase())) {
				suggests=this.generateAlternatives(word);
				if (suggests.isEmpty()) {
					suggests=generateAlternatives(word.toLowerCase());
				}
				this.errors.put(word,suggests);
			}
			this.parser.move();
		}
	}


	/**
	 * @param s the word to modify
	 * @return an ArrayList containing all the corrections
	 */
	private ArrayList<String> generateAlternatives(String s) {
		ArrayList<String> suggests = new ArrayList<String>();
		int k = s.length();
		String toAdd="";
		for (int i = 0; i < k; i++) {

			String left = s.substring(0, i);
			String right = s.substring(i + 1, k);

			// removeChar
			toAdd=this.checkRemovingChar(left,right);
			if (toAdd != null && !suggests.contains(toAdd)) suggests.add(toAdd);

			// switchChar
			toAdd=this.checkSwitchingChar(i, k, s, left, right);
			if (toAdd != null && !suggests.contains(toAdd)) suggests.add(toAdd);

			// changeChar & addChar
			for (char c = 'a'; c <= 'z'; c++) {
				toAdd=c+s;
				if (this.dictionaries.contains(toAdd) && !suggests.contains(toAdd)) suggests.add(toAdd);
				toAdd=s+c;
				if (this.dictionaries.contains(toAdd) && !suggests.contains(toAdd)) suggests.add(toAdd);
				toAdd=checkChangingCharLow(c, left, right);
				if (toAdd != null && !suggests.contains(toAdd)) suggests.add(toAdd);
				toAdd=checkChangingCharUp(c, left, right);
				if (toAdd != null && !suggests.contains(toAdd)) suggests.add(toAdd);
				toAdd=checkAddingCharLow(i, c, s, left, right);
				if (toAdd != null && !suggests.contains(toAdd)) suggests.add(toAdd);
				toAdd=checkAddingCharUp(i, c, s, left, right);
				if (toAdd != null && !suggests.contains(toAdd)) suggests.add(toAdd);
			}

		}
		return suggests;
	}

	/**
	 * @param left The left part of the word
	 * @param right The right part of the word
	 * @return The concatenation of the two String if it's in a dictionary
	 */
	private String checkRemovingChar(String left, String right) {
		if (this.dictionaries.contains(left + right)) {
			return left+right;
		} else {
			return null;
		}
	}

	/**
	 * @param i Index of first letter to switch
	 * @param k Index of second letter to switch
	 * @param s The word in wich to switch two letters
	 * @param left The left part of the word
	 * @param right The right part of the word
	 * @return The word with its "i"th and "k"th letters switched
	 */
	private String checkSwitchingChar(int i, int k, String s, String left,
			String right) {
		if (i + 1 < k && this.dictionaries.contains(left + right.substring(0, 1)+ s.charAt(i) + right.substring(1))) {
			return left + right.substring(0, 1) + s.charAt(i)
			+ right.substring(1);
		} else {
			return null;
		}
	}

	/**
	 * @param c The low char to replace in the word	
	 * @param left The left part of the word
	 * @param right The right part of the word
	 * @return The word with the "c" character added instead of an other character
	 */
	private String checkChangingCharLow(char c, String left, String right) {
		if (this.dictionaries.contains(left + c + right)) {
			return left + c + right;
		} else {
			return null;
		}
	}

	/**
	 * @param i The index where add the letter
	 * @param c The low character to add
	 * @param s The word to modify
	 * @param left The left part of the word
	 * @param right The right part of the word
	 * @return The word with the "c" character added
	 */
	private String checkAddingCharLow(int i, char c, String s, String left, String right) {
		//cas particulier, i=0
		if (i==0 && this.dictionaries.contains(c+right)) {
			return c+right;
		}
		else if (this.dictionaries.contains(left + s.charAt(i) + c + right)) {
			return left + s.charAt(i) + c + right;
		} else {
			return null;
		}
	}

	/**
	 * @param c The up char to replace in the word	
	 * @param left The left part of the word
	 * @param right The right part of the word
	 * @return The word with the "c" character added instead of an other character
	 */
	private String checkChangingCharUp(char c, String left, String right) {
		if (this.dictionaries.contains(left + (char) (c + 32) + right)) {
			return left + (char) (c + 32) + right;
		} else {
			return null;
		}
	}

	/**
	 * @param i The index where add the letter
	 * @param c The up character to add
	 * @param s The word to modify
	 * @param left The left part of the word
	 * @param right The right part of the word
	 * @return The word with the "c" character added
	 */
	private String checkAddingCharUp(int i, char c, String s, String left, String right) {
		if (i==0 && this.dictionaries.contains(c+right)) {
			return c+right;
		}
		else if (this.dictionaries.contains(left + s.charAt(i) + (char) (c + 32) + right)) {
			return left + s.charAt(i) + (char) (c + 32) + right;
		} else {
			return null;
		}
	}

	/**
	 * @return the dictionaries
	 */
	 public DictionarySet getDictionaries() {
		return dictionaries;
	}



}